package com.econ.powercloud.ems.util;

import com.econ.powercloud.common.base.facade.enums.StatusType;
import com.econ.powercloud.common.base.facade.exception.CommonRuntimeException;
import com.econ.powercloud.common.base.facade.pojo.MsgResult;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.time.DateFormatUtils;
import org.apache.commons.lang3.StringUtils;
import org.jxls.common.Context;
import org.jxls.util.JxlsHelper;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.Assert;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Collection;
import java.util.Date;
import java.util.Map;

/**
 * 导出Excel工具类
 *
 * @author chenxiaoqi
 * @since 2021/08/04
 */
@Slf4j
public class ExportExcelUtils {

    private static final String REPLACE_STR = "模板";

    private ExportExcelUtils() {
    }

    /**
     * 导出Excel 支持直接返回模板 当params为null时
     * @param params       参数 如 key为items则 excel模板中就可以使用items参数
     * @param templatePath 模板路径 是resources的相对路径
     * @param fileName     文件名称 用于返回给前端的名称 会自动带上具体到分钟的时间
     * @return
     */
    public static ResponseEntity<Resource> export(Map<String, Object> params, String templatePath, String fileName) {
        Assert.isTrue(StringUtils.isNotBlank(templatePath), "模板path不能为空");
        Assert.isTrue(StringUtils.isNotBlank(fileName), "文件名不能为空");
        if (params == null) {
            params = Maps.newHashMap();
        } else {
            Boolean flag = true;
            for (Object o : params.values()) {
                if (!checkObject(o)) {
                    flag = false;
                }
            }
            if (flag) {
                throw new CommonRuntimeException(StatusType.OK, "数据为空，不可导出");
            }
            // 有数据说明不是模板 需要把模板文案去掉
            fileName = fileName.replace(REPLACE_STR, "") + "_" + DateFormatUtils.format(new Date(), "yyyyMMddHHmm");
        }
        try {
            Resource resource = new ClassPathResource(templatePath);
            String suffix = templatePath.substring(templatePath.lastIndexOf("."));
            fileName += suffix;
            ByteArrayOutputStream output = new ByteArrayOutputStream();
            params.put("fn", new CustomFunction());
            JxlsHelper.getInstance().processTemplate(resource.getInputStream(), output, new Context(params));
            byte[] bytes = output.toByteArray();
            ByteArrayResource byteArrayResource = new ByteArrayResource(bytes);

            return ResponseEntity.ok()
                    .contentLength(bytes.length)
                    .header(HttpHeaders.CONTENT_DISPOSITION, String.format("attachment; filename=%s",
                            URLEncoder.encode(fileName, Charset.defaultCharset().name())))
                    .contentType(MediaType.APPLICATION_OCTET_STREAM)
                    .body(byteArrayResource);
        } catch (Exception e) {
            log.error("导出excel异常，templatePath:{}, fileName:{}", templatePath, fileName, e);
            throw new CommonRuntimeException(StatusType.FAIL, "导出Excel异常，异常信息：" + e.getMessage());
        }
    }

    /**
     * 下载到服务器指定位置
     * @param params
     * @param templatePath
     * @param fileName
     * @param path 不考虑window环境
     */
    public static MsgResult export(Map<String, Object> params, String templatePath, String fileName, String path) {
        Assert.isTrue(StringUtils.isNotBlank(templatePath), "模板path不能为空");
        Assert.isTrue(StringUtils.isNotBlank(fileName), "文件名不能为空");
        if (params == null) {
            params = Maps.newHashMap();
        }
        mkdirFile(path);
        ByteArrayOutputStream output = new ByteArrayOutputStream();
        FileOutputStream fileOutputStream = null;
        try {
            Resource resource = new ClassPathResource(templatePath);
            String suffix = templatePath.substring(templatePath.lastIndexOf("."));
            fileName += suffix;
            params.put("fn", new CustomFunction());
            JxlsHelper.getInstance().processTemplate(resource.getInputStream(), output, new Context(params));
            fileOutputStream = new FileOutputStream(path + "/" + fileName);
            fileOutputStream.write(output.toByteArray());
            return MsgResult.builder().status(StatusType.OK.getCode()).statusText(StatusType.OK.getStatusText()).build();
        } catch (Exception e) {
            log.error("导出excel异常，templatePath:{}, fileName:{}", templatePath, fileName, e);
            return MsgResult.builder().status(StatusType.FAIL.getCode()).statusText("导出Excel异常，异常信息：" + e.getMessage()).build();
        }finally {
            if(fileOutputStream != null){
                try {
                    fileOutputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(output != null){
                try {
                    output.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static void mkdirFile(String path){
        if(path.endsWith("/")){
            path = path.substring(0,path.length()-1);
        }
        File file = new File(path);
        if(!file.exists()){
            file.mkdirs();
        }
    }

    public static boolean checkObject(Object object) {
        if (object == null) {
            return true;
        }
        if (object instanceof CharSequence) {
            return ((CharSequence) object).length() == 0;
        }
        if (object instanceof Collection) {
            return ((Collection) object).isEmpty();
        }
        if (object instanceof Map) {
            return ((Map) object).isEmpty();
        }
        if (object instanceof Object[]) {
            return ((Object[]) object).length == 0;
        }
        return false;
    }

    /**
     * 自定义的一些方法 用于导出时用
     *
     * @author chenxiaoqi
     * @since 2021/09/17
     */
    public static class CustomFunction {

        /**
         * 格式化
         * @param date
         * @param format
         * @return
         */
        public String format(Object date, String format) {
            if (date == null) {
                return "";
            }
            if (date instanceof LocalDateTime) {
                return ((LocalDateTime) date).format(DateTimeFormatter.ofPattern(format));
            }
            if (date instanceof Date) {
                return DateFormatUtils.format(((Date) date), format);
            }
            return "";
        }

        /**
         * 减法
         * @param left
         * @param right
         * @return
         */
        public Number subtract(Number left, Number right) {
            if (left == null) {
                return null;
            }
            if (right == null) {
                return left;
            }
            BigDecimal subtract = new BigDecimal(left.toString()).subtract(new BigDecimal(right.toString()));
            if (left instanceof Integer && right instanceof Integer) {
                return subtract.intValue();
            }
            return subtract.doubleValue();
        }
    }
}
